package ar.uade.fain.ai.tpo.dao.db;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

public class ConnectionPool {

	private final static String CONF_PATH_SYS_PROPERTY = "db.conf.path";
	
	private final static String DEFAULT_CONF_PATH = "db.properties";

	private Properties configuracion;
	private static ConnectionPool instancia;
	private Set<Connection> connections;
	private Integer poolSize;
	private String url;
	private String username;
	private String password;

	private ConnectionPool() {
		super();
	}

	public Properties getConfiguracion() {
		return configuracion;
	}

	public static ConnectionPool getInstancia() {
		if (instancia == null) {
			instancia = new ConnectionPool();
			instancia.init();
		}
		return instancia;
	}

	protected Set<Connection> getConnections() {
		return connections;
	}

	public Connection getConnection() {
		if (getConnections() == null) {
			throw new IllegalStateException(
					"DB: Debe inicializar el pool primero.");
		}
		if (getConnections().isEmpty()) {
			return null;
		}
		Connection connection = getConnections().iterator().next();
		getConnections().remove(connection);
		return connection;
	}

	public void release(Connection connection) {
		try {
			if (connection.isClosed()) {
				createConnection();
			}
			else{
				getConnections().add(connection);
			}
		} catch (Exception e) {
			System.err.println("DB: No se puede crear nueva conexion."
					+ e.getMessage());
		}
	}

	public void init() {
		if (getConnections() != null)
			return;
		this.connections = new HashSet<Connection>();
		loadConfiguracion();
		final String driverClassName = getConfiguracion().get("db.driverClass")
				.toString();
		this.url = getConfiguracion().get("db.url").toString();
		this.username = getConfiguracion().get("db.username").toString();
		this.password = getConfiguracion().get("db.password").toString();
		this.poolSize = Integer.valueOf(getConfiguracion().get("pool.size")
				.toString());
		System.out.println("DB: Configuracion de conexion" + url + username
				+ password);
		try {
			Class.forName(driverClassName);
			for (int i = 0; i < poolSize; i++) {
				createConnection();
			}

		} catch (ClassNotFoundException e) {
			System.err.println("DB: El driver no esta presente" + e);
			throw new IllegalStateException(e);
		} catch (SQLException e) {
			System.err.println("DB: No se pudo conectar con la base" + e);
			throw new IllegalStateException(e);
		}
		System.out.println("DB: Pool de conexiones inicializado correctamente");
	}

	private Connection createConnection() throws SQLException {
		Connection connection = DriverManager.getConnection(url, username,
				password);
		getConnections().add(connection);
		return connection;
	}

	public void destroy() {
		for (Connection connection : getConnections()) {
			try {
				connection.close();
			} catch (Exception e) {
				System.err.println("DB: Error cerrando conexion: " 
						+ e.getMessage());
			}
		}
	}

	private void loadConfiguracion() {
		String externalPath = System.getProperty(CONF_PATH_SYS_PROPERTY);
		InputStream resource = getClass().getClassLoader()
				.getResourceAsStream(DEFAULT_CONF_PATH);
		if (externalPath != null) {
			try {
				resource = new FileInputStream(externalPath);
			} catch (FileNotFoundException e) {
				System.out.println(
						"DB: Configuracion externa no valida, tomando defaults.");
			}
		} else {
			System.out.println(
					"DB: Configuracion externa no presente, tomando defaults.");
		}
		if (resource != null) {
			this.configuracion = new Properties();
			try {
				this.configuracion.load(resource);
			} catch (Exception e) {
				throw new IllegalStateException(
						"DB: No se puede acceder a la configuracion.", e);
			}
		} else {
			throw new IllegalStateException(
					"DB: Configuracion no presente en el sistema.");
		}
	}

}
